package community.peers.internetradio.network;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaPlayer;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import community.peers.internetradio.R;
import community.peers.internetradio.broadcastintent.PlayPause;
import community.peers.internetradio.broadcastintent.Stop;
import wseemann.media.FFmpegMediaMetadataRetriever;

public class Player {

    /**
     * Needed when we receive actions occuring in the notification
     * player (via BroadcastIntent). This identify the notification.
     */
    public static final int NOTIFICATION_ID = 0;

    protected static HashMap<String, String> radio;
    protected static MediaPlayer player;
    protected static Context context;

    /**
     * Timer to poll stream metadata every once in a while.
     * I don't know if Icecast supports push events, so I'm
     * sending a request for metadata every minute.
     */
    protected static Timer metadataTimer;

    /**
     * The notification that will be displayed in the notification drawer.
     */
    protected static NotificationCompat.Builder streamNotification;

    public static void playStream(
        Context theContext,
        HashMap<String, String> theRadio)
    {
        Player.stop();

        context = theContext;
        radio = theRadio;
        player = new MediaPlayer();

        try {
            player.setDataSource(radio.get("stream"));
            player.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
                @Override
                public void onBufferingUpdate(MediaPlayer mp, int percent) {
                }
            });
            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                // Player ready, start automatically
                Player.play();
                }
            });
            player.prepareAsync();

            Player.notifyNewStream();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Restart mediaplayer after pause
    public static void play()
    {
        if (player != null)
        {
            player.start();
            Player.fetchIcecastData();
        }
    }

    public static void pause()
    {
        if (player != null)
        {
            player.pause();
            Player.stopFetchingIcecastData();
        }
    }

    public static void playOrPause()
    {
        if (player != null)
            if (player.isPlaying())
            {
                player.pause();
                Player.stopFetchingIcecastData();
            } else {
                player.start();
                Player.fetchIcecastData();
            }
    }

    public static void stop()
    {
        if (player != null)
        {
            player.stop();
            player.release();
            player = null;

            Player.stopFetchingIcecastData();
        }
    }

    /**
     * Get Icecast data from the stream, if available.
     * The data from the stream has info about the current song.
     */
    public static void fetchIcecastData()
    {
        // Stop any previous task fetching data
        Player.stopFetchingIcecastData();

        /**
         * When a timer is no longer needed, users should call cancel(),
         * which releases the timer's thread and other resources. Timers
         * not explicitly cancelled may hold resources indefinitely.
         */
        Player.metadataTimer = new Timer();

        // Fetch data automatically every few minutes
        Player.metadataTimer.schedule(
            new TimerTask()
            {
                @Override
                public void run()
                {
                    try {
                        // Get the data
                        FFmpegMediaMetadataRetriever mdr = new FFmpegMediaMetadataRetriever();
                        mdr.setDataSource(Player.radio.get("stream"));
                        String icy = mdr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ICY_METADATA);
                        mdr.release();

                        /**
                         * Parse Icecast info.
                         * Data comes as StreamTitle='';StreamUrl='';...
                         * so we need to parse it.
                         */
                        Map<String, String> metadata = new HashMap();
                        String[] metaParts = icy.split(";");
                        Pattern p = Pattern.compile("^([a-zA-Z]+)=\\'(.*)\\'$");
                        Matcher m;
                        for (int i = 0; i < metaParts.length; i++)
                        {
                            m = p.matcher(metaParts[i]);
                            if (m.find())
                                metadata.put(m.group(1), m.group(2));
                        }

                        String streamTitle = metadata.get("StreamTitle").trim();

                        if (streamTitle != null && streamTitle.length() > 0)
                            Player.updateNotification(streamTitle, null);
                    } catch (Exception e) {}
                }
            },
            // Start after 0s, repeat after 60s
            0, 60000);
    }

    public static void stopFetchingIcecastData()
    {
        if (Player.metadataTimer != null)
        {
            Player.metadataTimer.cancel();
            Player.metadataTimer.purge();
            Player.metadataTimer = null;
        }
    }

    // Show a notification about the new stream
    protected static void notifyNewStream()
    {
        // Play/Pause intent when tapping the notification
        // PendingIntent.getBroadcast (Context context, int requestCode, Intent intent, int flags)
        Intent play_intent = new Intent(context, PlayPause.class);
        PendingIntent play_pintent = PendingIntent.getBroadcast(context, 0, play_intent, 0);

        // Stop intent when removing the notification
        Intent stop_intent = new Intent(context, Stop.class);
        PendingIntent stop_pintent = PendingIntent.getBroadcast(context, 0, stop_intent, 0);

        Player.streamNotification = new NotificationCompat.Builder(context)
            .setAutoCancel(false)
            // Show controls on lock screen even when user hides sensitive content.
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setSmallIcon(R.drawable.ic_graphic_eq)
            //.setLargeIcon()
            // Add media control buttons that invoke intents in your media service
            //.addAction(R.drawable.ic_play, "Play/Pause", play_pintent)
            //.addAction(R.drawable.ic_stop, "Stop", stop_pintent)
            .setContentIntent(play_pintent)
            .setDeleteIntent(stop_pintent)
            //.addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
            //.addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
            // Apply the media style template
            //.setStyle(new NotificationCompat.MediaStyle()
            //        .setShowActionsInCompactView(1 /* #1: pause button */)
            //        .setMediaSession(mMediaSession.getSessionToken()))
            .setContentTitle(radio.get("name"))
            .setContentText("Internet Radio")
            //.setLargeIcon(albumArtBitmap)
            .setTicker("Buffering... " + radio.get("name"))
            // Display datetime in notification
            .setShowWhen(true)
            // Show elapsed time instead of creation time
            .setUsesChronometer(true);

        // Sticky notification
        //theNotification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR

        // Gets an instance of the NotificationManager service
        NotificationManager mNotifyMgr =
            (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);

        // Builds the notification and issues it.
        mNotifyMgr.notify (
            Player.NOTIFICATION_ID,
            Player.streamNotification.build());

        updateNotificationLargeIcon();
    }

    // Change text when new Icecast data is available
    protected static void updateNotification(String content, Bitmap largeIcon)
    {
        // Content is changed to update song info
        if (content != null)
            Player.streamNotification
                .setContentText(content)
                .setTicker("Listening " + content);

        // Update large icon in notification
        if (largeIcon != null)
            Player.streamNotification
                .setLargeIcon(largeIcon);


        // Gets an instance of the NotificationManager service
        NotificationManager mNotifyMgr =
                (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);

        // Builds the notification and issues it.
        // Because the ID remains unchanged, the existing notification is updated
        mNotifyMgr.notify (
            Player.NOTIFICATION_ID,
            Player.streamNotification.build());
    }

    /**
     * Send a new async HTTP request to fetch radio image, and then
     * upload the notification with the new logo.
     */
    protected static void updateNotificationLargeIcon()
    {
        RequestQueue queue = Volley.newRequestQueue(context);
        ImageLoader imageLoader = new ImageLoader(queue, new ImageLoader.ImageCache() {
            @Override
            public Bitmap getBitmap(String url) {
                return null;
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {

            }
        });

        imageLoader.get(Player.radio.get("logo"), new ImageLoader.ImageListener() {
            @Override
            public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
                updateNotification(null, response.getBitmap());
            }

            @Override
            public void onErrorResponse(VolleyError error) {

            }
        });
    }
}